#include "CClientPull.h"
#include "RtpManager.h"
#include "RtcpManager.h"
#include "Nalu.h"
#include <QTime>
#include <QDebug>
#include <QThread>
#include <QTimerEvent>
#include <QHostAddress>
#include <QtNetwork>
#include <QNetworkDatagram>
#include <memory>
#include <cstdio>
using namespace RTSP;

#define DST_IP "127.0.0.1"

QByteArray getUdpRecvData(QUdpSocket *udpSocket);

CPullClient::CPullClient(QObject *parent) : QObject(parent),
                                            m_rtspTcp(nullptr)
{
    m_file = new QFile("../RTSP_Client_Qt/bin/log.txt");
    if (m_file->open(QIODevice::WriteOnly | QIODevice::Text))
        m_stream.setDevice(m_file);
    else
        qDebug() << "open file error";
}

CPullClient::~CPullClient()
{
    onEndConnect();
    m_stream.flush();
    m_file->flush();
    m_file->close();
}

#pragma region connect
void CPullClient::onStartConnect(const QString &url)
{
    qDebug() << "onStartConnect";
    if (m_rtspTcp == nullptr)
    { // 保证socket在子线程中创建
        m_rtspTcp = new QTcpSocket(this);
        // 接收消息
        connect(m_rtspTcp, &QTcpSocket::readyRead, this, &CPullClient::recvRtspMsg);
        // 若连接发出断开信号, 则调用断开函数
        connect(m_rtspTcp, &QTcpSocket::disconnected, this, &CPullClient::disconnected);
    }
    m_rtspManager.setRtspUrl(url.toUtf8());

    auto ip = url.mid(7, url.indexOf(':', 7) - 7);
    auto port = url.mid(url.indexOf(':', 7) + 1, url.indexOf('/', 7) - url.indexOf(':', 7) - 1).toUInt();
    // auto mediaRoute = m_rtspUrl.mid(m_rtspUrl.indexOf('/', 7) + 1);

    m_rtspTcp->connectToHost(ip, port);

    if (true == m_rtspTcp->waitForConnected(2000))
    { // 阻塞线程,直到连接 成功/失败
        emit CPullClient::connected();

        if (!sendCmd(m_rtspManager.getStartCmd()))
            this->onEndConnect(); // 发送失败, 关闭连接
    }
    else
    {
        emit CPullClient::disconnected();
    }
}

void CPullClient::onEndConnect()
{
    killRtspTimer();

    if (!m_rtspTcp || m_rtspTcp->state() != QAbstractSocket::ConnectedState)
        return;
    if (m_rtpUdp != nullptr)
    {
        qDebug() << "m_rtpUdp->close()" << m_rtpUdp->state();
        m_rtpUdp->disconnectFromHost();
        m_rtpUdp->close();
        m_rtpUdp->abort();
    }
    if (m_rtcpUdp != nullptr)
    {
        qDebug() << "m_rtcpUdp->close()" << m_rtcpUdp->state();
        m_rtcpUdp->disconnectFromHost();
        m_rtcpUdp->close();
        m_rtcpUdp->abort();
    }

    m_rtpBuf.clear();
    isSendSdp = false;
    sendCmd(m_rtspManager.getEndCmd());
    m_rtspTcp->flush();
    m_rtspTcp->disconnectFromHost(); // 关闭 socket
}
#pragma endregion

bool CPullClient::sendCmd(const QByteArray &cmd)
{
    reStartRtspTimer(2000);

    if (cmd.size() <= 0)
        return true;

    writeToLog(QString::asprintf("[send]%s : size=%lld, buf=\n%s \n", QTime::currentTime().toString("hh:mm:ss.zzz").toLatin1().data(), cmd.size(), cmd.data()));

    return m_rtspTcp->write(cmd) > 0;
}

#pragma region recv
void CPullClient::recvRtspMsg()
{
    if (m_rtspManager.getNextMethod() == OVER_PLAY && TCP == m_rtspManager.getSetupMode())
    { // TCP模式, 共用一条TCP连接
        m_rtpBuf += m_rtspTcp->readAll();
        int p = 0;
        while (m_rtpBuf.size() - p > 4)
        {
            if (m_rtpBuf[p] == '$')
            {
                m_rtpBuf.remove(0, p);
                p = 0;

                Interleaved *itld = (Interleaved *)m_rtpBuf.data();
                int packetLen = itld->getLength() + sizeof(Interleaved);
                if (packetLen <= m_rtpBuf.size())
                {
                    if (itld->channel % 2 == 0)
                        analyzeRtpData(m_rtpBuf.mid(sizeof(Interleaved), itld->getLength()));
                    else
                        analyzeRtcpData(m_rtpBuf.mid(sizeof(Interleaved), itld->getLength()));

                    m_rtpBuf.remove(0, packetLen);
                }
                else
                {
                    break;
                }
            }
            else
                p++;
        }
    }
    else
    { // 正常接收RTSP消息
        auto recvBuf = m_rtspTcp->readAll();

        writeToLog(QString::asprintf("[recv]%s : size=%lld, buf=\n%s \n", QTime::currentTime().toString("hh:mm:ss.zzz").toLatin1().data(), recvBuf.size(), recvBuf.data()));

        if (!sendCmd(m_rtspManager.parseReplyAndGetCmd(recvBuf)))
            qDebug() << "sendCmd error";

        if ((m_rtspManager.getNextMethod() == SETUP) && isSendSdp == false)
        {
            emit sendSdp(m_rtspManager.getSdp());
            isSendSdp = true;
        }

        if (m_rtspManager.getNextMethod() == OVER_PLAY)
        {
            const auto &raply = m_rtspManager.getCurRaplyData();
            killRtspTimer(); // 停止RTSP定时器, TCP模式也能触发

            if (raply.ResponseStateCode == 200 && UDP == m_rtspManager.getSetupMode())
            { // 建立UDP连接
                qDebug() << "setupMode == UDP";
                if (m_rtpUdp == nullptr)
                {
                    m_rtpUdp = new QUdpSocket(this);
                    m_rtpUdp->abort();
                    connect(m_rtpUdp, &QUdpSocket::readyRead, [this]()
                            { analyzeRtpData(getUdpRecvData(m_rtpUdp)); });
                }
                if (m_rtcpUdp == nullptr)
                {
                    m_rtcpUdp = new QUdpSocket(this);
                    m_rtcpUdp->abort();
                    connect(m_rtcpUdp, &QUdpSocket::readyRead, [this]()
                            { analyzeRtcpData(getUdpRecvData(m_rtcpUdp)); });

                    srand(time(NULL));
                    rtcp_ssrc = (rand() % 0xfe + 1) << 24 | (rand() % 0xfe + 1) << 16 | (rand() % 0xfe + 1) << 8 | (rand() % 0xfe + 1);
                }
                m_rtpUdp->bind(QHostAddress::LocalHost, m_rtspManager.getPort_1());
                m_rtcpUdp->bind(QHostAddress::LocalHost, m_rtspManager.getPort_2());
            }
        }
    }
}

void CPullClient::analyzeRtpData(const QByteArray &buf)
{
    if (buf.isEmpty())
        return;

    RtpHeader *rtpHeader = (RtpHeader *)buf.data();
    auto rtpPayload = buf.data() + sizeof(RtpHeader);

    auto sdpManager = m_rtspManager.getSdp();

    if (recv_rtp_count == 0) // 目的: 从获取到一帧关键帧后开始才开始解码
    {
        if (rtpHeader->payloadType == sdpManager->videoPayloadType && NalHeader::I_FRAME == ((NalHeader *)rtpPayload)->getPicCodedType())
        {
            qDebug() << "start";
            recv_rtp_count++;
            curVideoSeq = rtpHeader->getSeq() + 1;
            emit sendRtpData(buf, sdpManager->videoStreamId);
        }
    }
    else
    {
        recv_rtp_count++;
        if (rtpHeader->payloadType == sdpManager->videoPayloadType)
        {
            if (rtpHeader->getSeq() != curVideoSeq) // 后续用来调整udp丢包
            {
                qDebug() << rtpHeader->getSeq() << curVideoSeq;
                curVideoSeq = rtpHeader->getSeq() + 1;
            }
            else
            {
                curVideoSeq++;
            }

            emit sendRtpData(buf, sdpManager->videoStreamId);
        }
        else if (rtpHeader->payloadType == sdpManager->audioPayloadType)
            emit sendRtpData(buf, sdpManager->audioStreamId);
        else
            qDebug() << "recvRtpData error";
    }
}

void CPullClient::analyzeRtcpData(const QByteArray &buf)
{
    if (buf.isEmpty())
        return;
    RTCP::Header *rtcpHeader = (RTCP::Header *)buf.data();
    RTCP::SenderReport *sr = (RTCP::SenderReport *)(buf.data() + sizeof(RTCP::Header));
    qDebug() << "recvRtcpData: " << rtcpHeader->packetType << QString::number(qFromBigEndian(sr->ssrc), 16);
    emit sendRtcpData(buf);
}
#pragma endregion

#pragma region timerEvent
void CPullClient::timerEvent(QTimerEvent *event)
{
    if (event->timerId() == rtspTimerId)
        onEndConnect();
}

inline void CPullClient::killRtspTimer()
{
    if (rtspTimerId > 0)
        this->killTimer(rtspTimerId);
    rtspTimerId = 0;
}

inline void CPullClient::reStartRtspTimer(int ms)
{
    killRtspTimer();
    rtspTimerId = this->startTimer(ms);
}
#pragma endregion

// 将数据写入日志
inline void CPullClient::writeToLog(const QString &data)
{
    // 如果文件已经打开
    if (m_file->isOpen())
        // 将数据写入流
        m_stream << data;
}

// 获取UDP接收的数据(错误返回空数组)
inline QByteArray getUdpRecvData(QUdpSocket *udpSocket)
{
    QByteArray recvBuf;
    if (udpSocket->hasPendingDatagrams() && udpSocket->pendingDatagramSize() > 0)
    {
        // 获取UDP套接字中待接收的数据包的大小
        auto size = udpSocket->pendingDatagramSize();
        // 创建一个QByteArray对象，大小为待接收的数据包大小，初始值为0
        recvBuf.resize(size);
        // 从UDP套接字中读取数据包，存入recvBuf中
        if (size != udpSocket->readDatagram(recvBuf.data(), size))
        {
            // LOGE("recvDatagram error: %d", WSAGetLastError());
        }
    }
    // 返回接收到的数据包
    return recvBuf;
}
